package org.easymodelers.gmf.template;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;

public abstract class DynamicModelManager {
	private static String firstUpper(String s) {
		return s.substring(0, 1).toUpperCase() + s.substring(1);
	}
	
	public static EObject createEObject(Object factory, String typeName)
							throws IllegalArgumentException, IllegalAccessException,
							InvocationTargetException, SecurityException, NoSuchMethodException {
		String createMethodName = "create" + typeName;
		Method createMethod = factory.getClass().getDeclaredMethod(createMethodName);
		return (EObject) createMethod.invoke(factory);
	}

	public static boolean isEListAttribute(EObject parent, String name) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		System.out.println("> isEListAttribute - parent = "+parent+", name = "+name);
		Class clazz = parent.getClass();
		String getMethodName = "get" + firstUpper(name);
		System.out.println("getMethodName = "+getMethodName);
		Method getMethod = clazz.getMethod(getMethodName);
		Class fieldType = getMethod.getReturnType();
		System.out.println("fieldType = "+fieldType);

		return Collection.class.isAssignableFrom(fieldType);
	}
	
	public static EList getEListAttribute(EObject parent, String name) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		Class clazz = parent.getClass();
		String getMethodName = "get" + firstUpper(name);
		System.out.println("getMethodName = "+getMethodName);
		Method getMethod = clazz.getMethod(getMethodName);
		return (EList) getMethod.invoke(parent);
	}
	
	public static Object getAttribute(EObject o, String name) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		Class clazz = o.getClass();
		String getMethodName = "get" + firstUpper(name);
		System.out.println("getMethodName = "+getMethodName);
		Method getMethod = clazz.getMethod(getMethodName);
		return getMethod.invoke(o);
	}
	
	public static Object setAttribute(EObject o, String name, Object value) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		Class clazz = o.getClass();
		System.out.println("clazz = "+clazz);
		String setMethodName = "set" + firstUpper(name);
		System.out.println("setMethodName = "+setMethodName);
		System.out.println("value = "+value);
		Method setMethod = null;
		for (Method method : clazz.getMethods()) {
			//System.out.println(" -> method.getName() = "+method.getName());
			if (setMethodName.equals(method.getName())) {
				setMethod = method;
			}
		}
		Class[] parameterTypes = setMethod.getParameterTypes();
		for (Class class1 : parameterTypes) {
			System.out.println("parameter = "+class1);
		}
		return setMethod.invoke(o, value);
	}
	
	public static void setEObject(EObject parent, EObject o, String name) throws SecurityException, NoSuchFieldException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		System.out.println("> setEObject");

		if (isEListAttribute(parent, name)) {
			System.out.println("  -> collection");
			Collection c = (Collection) getEListAttribute(parent, name);
			c.add(o);
		} else {
			System.out.println("  -> not collection");
			String setMethodName = "set" + firstUpper(name);
			System.out.println("setMethodName = "+setMethodName);
			System.out.println("setMethodName param = "+o.getClass());
			Class parentClass = parent.getClass();
			Method setMethod = null;
			for (Method method : parentClass.getDeclaredMethods()) {
				if (setMethodName.equals(method.getName())) {
					setMethod = method;
				}
			}
			//parentClass.getDeclaredMethod(setMethodName, o.getClass());
			setMethod.invoke(parent, o);
		}
	}
	
	public static boolean checkEObject(EObject o, Map<String,String> attributes) throws SecurityException, IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		for (String attributeName : attributes.keySet()) {
			String attributeValue = attributes.get(attributeName);
			String eobjectValue = (String) getAttribute(o, attributeName);
			if (!attributeValue.equals(eobjectValue)) {
				return false;
			}
		}
		return true;
	}

	public static boolean hasEObjectInList(EList l, Map<String,String> attributes) throws SecurityException, IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		for (Iterator i = l.iterator(); i.hasNext(); ) {
			EObject o = (EObject) i.next();
			if (checkEObject(o, attributes)) {
				return true;
			}
		}
		return false;
	}

	private static boolean checkEObjectType(EObject o, String type) {
		System.out.println("> checkEObjectType - o = "+o.getClass().getName()+", type = "+type);
		String oType = o.getClass().getName();
		int index = oType.lastIndexOf(".");
		oType = oType.substring(index+1);
		if (oType.endsWith("Impl")) {
			oType = oType.substring(0, oType.length()-4);
		}
		
		System.out.println("oType = "+oType);
		return oType.equals(type);
	}
	
	public static EObject findEObject(EObject rootEObject, String type, String fieldName, String fieldValue) throws SecurityException, IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		System.out.println("> findEObject - fieldName = "+fieldName+", fieldValue = "+fieldValue);
		for (TreeIterator i = rootEObject.eAllContents(); i.hasNext(); ) {
			EObject o = (EObject) i.next();
			Class clazz = o.getClass();
			if (checkEObjectType(o, type)) {
				String attributeValue = (String) getAttribute(o, fieldName);
				System.out.println(" -> attributeValue = "+attributeValue+", fieldValue = "+fieldValue);
				if (attributeValue.equals(fieldValue)) {
					System.out.println("match!");
					return o;
				}
			}
		}
		return null;
	}
}
